package com.reger.saio.core;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousChannelGroup;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.reger.saio.CompletedCallBack;
import com.reger.saio.FailedCallBack;

public class Client<T> {
	private static final Logger log = LogManager.getLogger(Client.class);

	private AsynchronousChannelGroup group;
	private AsynchronousSocketChannel client;
	private InetSocketAddress serverAddress;
	private T attachment;

	public Client() {
		super();

	}

	public Client<T> connect() {
		try {
			this.client = AsynchronousSocketChannel.open(group);
		} catch (IOException e) {
			throw new RuntimeException(e);
		}

		CountDownLatch downLatch = new CountDownLatch(1);
		List<Throwable> throwables = new ArrayList<>();
		this.client.connect(this.serverAddress, this.attachment, new CompletionHandler<Void, T>() {
			@Override
			public void completed(Void result, T attachment) {
				downLatch.countDown();
			}

			@Override
			public void failed(Throwable exc, T attachment) {
				throwables.add(exc);
				downLatch.countDown();
			}
		});
		try {
			downLatch.await();
			if (!throwables.isEmpty()) {
				throw new RuntimeException(throwables.get(0));
			}
			return this;
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
	}

	public void close() {
		if (client.isOpen()) {
			try {
				client.close();
			} catch (IOException e) {
				throw new RuntimeException(e);
			}
		}
	}

	public Client<T> receive(final ByteBuffer buffer, CompletedCallBack<Integer, T> completed,
			FailedCallBack<T> failed) {
		client.read(buffer, attachment, new CompletionHandler<Integer, T>() {

			@Override
			public void completed(Integer result, T attachment) {
				buffer.flip();
				if (completed != null) {
					completed.completed(result, attachment);
				} else {
					log.info("从{}接受数据完毕", serverAddress);
				}
				buffer.clear();
				client.read(buffer, attachment, this);
			}

			@Override
			public void failed(Throwable exc, T attachment) {
				if (failed != null) {
					failed.failed(exc, attachment);
				} else {
					log.info("从{}接受数据失败", serverAddress);
				}
			}

		});
		return this;
	}

	public Client<T> send(final ByteBuffer buffer, CompletedCallBack<Integer, T> completed, FailedCallBack<T> failed) {
		client.write(buffer, attachment, new CompletionHandler<Integer, T>() {
			@Override
			public void completed(Integer result, T attachment) {
				if (completed != null) {
					completed.completed(result, attachment);
				} else {
					log.info("向{}发送数据完毕", serverAddress);
				}
			}

			@Override
			public void failed(Throwable exc, T attachment) {
				if (failed != null) {
					failed.failed(exc, attachment);
				} else {
					log.info("向{}发送数据失败", serverAddress);
				}
			}

		});
		return this;
	}

	public static <T> Client.Bulider<T> newBulider() {
		return new Client.Bulider<>();
	}

	public static class Bulider<T> {
		private AsynchronousChannelGroup group;
		private String host = "127.0.0.1";
		private int port = 8080;
		private T attachment;

		public Bulider<T> host(String host) {
			this.host = host;
			return this;
		}

		public Bulider<T> port(int port) {
			this.port = port;
			return this;
		}

		public Bulider<T> group(AsynchronousChannelGroup group) {
			this.group = group;
			return this;
		}

		public Bulider<T> attachment(T attachment) {
			this.attachment = attachment;
			return this;
		}

		public Client<T> build() {
			Client<T> client = new Client<T>();
			client.serverAddress = new InetSocketAddress(host, port);
			client.attachment = attachment;
			client.group = group;
			return client;
		}
	}
}
